
//#define COCOS2D_DEBUG 2

#include <cocos2d.h>
#include "CCIceSession.h"
#include "SessionHelper.h"
#include "Ice/ConnectionI.h"

IceDispatcher::IceDispatcher(const Ice::LoggerPtr& logger):_logger(logger)
{
	
}

void IceDispatcher::dispatch(const Ice::DispatcherCallPtr& call, const Ice::ConnectionPtr&)
{
	IceUtil::LockT<IceUtil::Mutex> lock(_dispatchCallMutex);

	_dispatcherCallList.push_back(new Ice::DispatcherCallPtr(call));
}

void IceDispatcher::update(){

	IceUtil::LockT<IceUtil::Mutex> lock(_dispatchCallMutex);

	//if(_logger != 0){
	//Ice::Warning out(_logger);
	//out << "update" ;
	//}

	//std::cout << "IceDispatcher::update" <<std::endl;


	std::vector<Ice::DispatcherCallPtr*>::const_iterator it = _dispatcherCallList.begin();

	for (; it != _dispatcherCallList.end(); ++it)
	{
		(**it)->run();
		delete *it;
	}

	_dispatcherCallList.clear();
}



/*-----------------------------------------------------------------------------------------*/


class SessionPingThread : virtual public IceUtil::Shared
{
public:
	virtual ~SessionPingThread() {}
	virtual void done() = 0;
};
typedef IceUtil::Handle<SessionPingThread> SessionPingThreadPtr;

/*-----------------------------------------------------------------------------------------*/

class CreatedCommunicatorCall :public Ice::DispatcherCall
{
public:
	CreatedCommunicatorCall(SessionCallback* callback,SessionHelper* sessionHelper)
		:_callback(callback),
		_sessionHelper(sessionHelper)
	{

	}
	virtual ~CreatedCommunicatorCall() 	{

	}


	virtual void run(){
		_callback->createdCommunicator(_sessionHelper);
	}
private:
	SessionHelper*     _sessionHelper;
	SessionCallback*   _callback;
};

/*-----------------------------------------------------------------------------------------*/

class DisconnectedCall :public Ice::DispatcherCall
{
public:
	DisconnectedCall(SessionCallback* callback,SessionHelper* sessionHelper)
		:_callback(callback),
		_sessionHelper(sessionHelper)
	{

	}
	virtual ~DisconnectedCall() 	{

	}


	virtual void run(){
		_callback->disconnected(_sessionHelper);
	}
private:
	SessionHelper*     _sessionHelper;
	SessionCallback*   _callback;
};
/*-----------------------------------------------------------------------------------------*/

class ConnectFailedCall :public Ice::DispatcherCall
{
public:
	ConnectFailedCall(SessionCallback* callback,SessionHelper* sessionHelper, const Ice::Exception* ex)
		:_callback(callback),
		_sessionHelper(sessionHelper),
		_exception(ex->ice_clone())
	{
	}

	virtual ~ConnectFailedCall() 	{
		_callback = NULL;
	}


	virtual void run(){
		_callback->connectFailed(_sessionHelper, *_exception);
	}
private:
	SessionHelper*     _sessionHelper;
	SessionCallback*   _callback;
	std::auto_ptr<Ice::Exception> _exception;
};


/*-----------------------------------------------------------------------------------------*/

class ConnectedCall :public Ice::DispatcherCall
{
public:
	ConnectedCall(SessionCallback* callback,SessionHelper* sessionHelper)
		:_callback(callback),
		_sessionHelper(sessionHelper)
	{

	}
	virtual ~ConnectedCall() 	{
		_callback = NULL;
	}


	virtual void run(){
		try{
			_callback->connected(_sessionHelper);
		}
		catch(const Glacier2::SessionNotExistException& ex){
			_sessionHelper->destroy();
		}
	}
private:
	SessionHelper*     _sessionHelper;
	SessionCallback*   _callback;
};



class DestroyInternalThread : virtual public IceUtil::Thread
{
public:
	DestroyInternalThread(SessionHelper* session)
		: IceUtil::Thread()
		, _session(session)			
	{
	}

	virtual ~DestroyInternalThread(){

	}
	void run(){	
		_session->destroyInternal();	
	}

private:
	SessionHelper* _session;
};
/*-----------------------------------------------------------------------------------------*/


class ConnectionThread : virtual public IceUtil::Thread
{
public:
	ConnectionThread(SessionHelper* session, SessionCallback* cb, const std::string& account, const std::string& passwd)
		: IceUtil::Thread()
		, _session(session)
		, _cb(cb)
		, _account(account)
		, _passwd(passwd)
	{
	}

	virtual ~ConnectionThread(){

	}
	void run()
	{
    //    CCLOG("ConnectionThread::run");
		try
		{

			_session->dispatchCallbackAndWait(new CreatedCommunicatorCall(_cb,_session));

			Glacier2::RouterPrx routerPrx = Glacier2::RouterPrx::uncheckedCast(_session->_communicator->getDefaultRouter());
            
       //     CCLOG("Glacier2 createSession");
			Glacier2::SessionPrx session = routerPrx->createSession(_account, _passwd);
         //   CCLOG("Glacier2 connected");
			_session->connected(routerPrx,session);
            

		}
		catch(const Ice::Exception& ex){
			try
			{
				_session->_communicator->destroy();
				_session->_communicator = NULL;
			}
			catch(const std::exception ex1)
			{
			}

			_session->dispatchCallback(new ConnectFailedCall(_cb,_session, &ex),NULL);
		}
	}

private:
	SessionHelper* _session;
	std::string _account;
	std::string _passwd;
	SessionCallback* _cb;

};


/*-----------------------------------------------------------------------------------------*/



SessionHelper::SessionHelper(SessionCallback* callback, Ice::InitializationData* initData)
	:_callback(callback),
	_initData(initData),
	_destroy(false),
	_connected(false)	
{
	_stats = new SessionStats();
	initData->stats = _stats;
}

SessionHelper::~SessionHelper(void)
{
	//IceUtil::LockT<IceUtil::Mutex> lock(_sessionMutex);
	if(_initData){
		if(_initData->dispatcher){
			 _initData->dispatcher = NULL;
		}
		delete _initData;
	}

	if(_callback){
		delete _callback;
	}

	
}

void SessionHelper::destroy()
{

	{
		IceUtil::LockT<IceUtil::Mutex> lock(_sessionMutex);

		if(_destroy)
		{
			return;
		}
		_destroy = true;
		if(!_connected)
		{
			//
			// In this case a connecting session is being
			// destroyed. The communicator and session will be
			// destroyed when the connection establishment has
			// completed.
			//
			return;
		}
		_session = NULL;

		//try
		//{
		//	Runtime.getRuntime().removeShutdownHook(_shutdownHook);
		//}
		//catch(IllegalStateException ex)
		//{
		//	// Ignore
		//}
		//catch(SecurityException ex)
		//{
		//	// Ignore
		//}
	}

	//
	// Run the destroyInternal in a thread. This is because it
	// destroyInternal makes remote invocations.
	//
	(new DestroyInternalThread(this))->start();

}



void SessionHelper::connect( const std::string username,const std::string password )
{
	assert(!_destroy);

	try
	{
    //    CCLOG("ice initilize");
		_communicator = Ice::initialize(*_initData);
		
	}
	catch(const Ice::LocalException& ex)
	{
    //    CCLOG("ice initilize failed");
		_destroy = true;
		dispatchCallback(new ConnectFailedCall(_callback,this, &ex),NULL);
		return;
	}

	ConnectionThread* connect_thread = new ConnectionThread(this,_callback, username, password);
    
  //  CCLOG("create connect thread");
	connect_thread->start();
}

void SessionHelper::connectImpl( const ConnectStrategy& factory )
{

}

void SessionHelper::dispatchCallback( Ice::DispatcherCall* call,Ice::ConnectionPtr conn )
{
	if(_initData->dispatcher){
		_initData->dispatcher->dispatch(call,conn);
	}
	else{
		call->run();
		delete call;
	}
}



class DispatchCallbackAndWaitThread : public  Ice::DispatcherCall
{
public:
	DispatchCallbackAndWaitThread(SessionHelper* session,  Ice::DispatcherCall* call,   IceUtil::Monitor<IceUtil::Mutex>& monitor)
		: _session(session)
		, _monitor(monitor)
		,_call(call)

	{
	}

	virtual ~DispatchCallbackAndWaitThread(){

	}
	void run()
	{	
		IceUtil::Monitor<IceUtil::Mutex>::Lock lock(_monitor);
		_call->run();

		delete _call;
		_monitor.notifyAll();
	}

private:
	SessionHelper* _session;
	IceUtil::Monitor<IceUtil::Mutex>& _monitor;
	Ice::DispatcherCall*  _call;	
};

void SessionHelper::dispatchCallbackAndWait( Ice::DispatcherCall* call )
{
	if(_initData->dispatcher)
	{
		IceUtil::Monitor<IceUtil::Mutex>  monitor ;

		IceUtil::Monitor<IceUtil::Mutex>::Lock lock(monitor);
		_initData->dispatcher->dispatch(new DispatchCallbackAndWaitThread(this,call,monitor), NULL);
		monitor.wait();
	
	}
	else
	{
		call->run();
		delete call;
	}
}

void SessionHelper::connected( Glacier2::RouterPrx router, Glacier2::SessionPrx session )
{
	Ice::ConnectionPtr conn = router->ice_getCachedConnection();
	_refreshPeriod = router->getSessionTimeout() / 2;
	std::string category = router->getCategoryForClient();

	{


		IceUtil::LockT<IceUtil::Mutex> lock(_sessionMutex);

		_router = router;

		if(_destroy)
		{
			//
			// Run the destroyInternal in a thread. This is because it
			// destroyInternal makes remote invocations.
			//
			(new DestroyInternalThread(this))->start();
			return;
		}

		//
		// Cache the category.
		//
		_category = category;

		//
		// Assign the session after _destroy is checked.
		//
		_session = session;
		_connected = true;

		objectAdapter();
	}

	dispatchCallback(new ConnectedCall(_callback,this),conn);
}

void SessionHelper::destroyInternal()
{
	assert(_destroy);
	Glacier2::RouterPrx router = NULL;
	Ice::CommunicatorPtr communicator = NULL;
	{

		IceUtil::LockT<IceUtil::Mutex> lock(_sessionMutex);

		if(!_router)
		{
			return;
		}

		router = _router;
		_router = NULL;

		communicator = _communicator;
		_communicator = NULL;
	}

	assert(communicator);

	try
	{
		router->destroySession();
	}
	catch(Ice::ConnectionLostException e)
	{
		//
		// Expected if another thread invoked on an object from the session concurrently.
		//
	}
	catch(Glacier2::SessionNotExistException e)
	{
		//
		// This can also occur.
		//
	}
	catch(const std::exception& e)
	{
		//
		// Not expected.
		//
		//	communicator->getLogger()->warning("SessionHelper: unexpected exception when destroying the session:\n" + e.what());
	}
	_connected = false;

	try
	{
		communicator->destroy();
	}
	catch(const std::exception& ex)
	{
	}

	//
	// Notify the callback that the session is gone.
	//
	dispatchCallback(new DisconnectedCall(_callback,this),NULL);

}

Ice::CommunicatorPtr SessionHelper::communicator()
{
	IceUtil::LockT<IceUtil::Mutex> lock(_sessionMutex);
	return _communicator;
}

Glacier2::SessionPrx SessionHelper::session()
{
	IceUtil::LockT<IceUtil::Mutex> lock(_sessionMutex);
	return _session;
}

bool SessionHelper::isConnected()
{
	IceUtil::LockT<IceUtil::Mutex> lock(_sessionMutex);
	return _connected;
}

Ice::ObjectAdapterPtr
SessionHelper::objectAdapter()
{
    if(!_router)
    {
        Glacier2::SessionNotExistException ex;
        throw ex;
    }

    IceUtil::Mutex::Lock lock(*IceInternal::Application::mutex);
    if(!_adapter)
    {
        _adapter = communicator()->createObjectAdapterWithRouter("", _router);
        _adapter->activate();
    }
    return _adapter;
}

Ice::ObjectPrx SessionHelper::addWithUUID( Ice::ObjectPtr servant )
{
    return objectAdapter()->add(servant, createCallbackIdentity(IceUtil::generateUUID()));
}

Ice::Identity
SessionHelper::createCallbackIdentity(const  std::string& name)
{
    Ice::Identity id;
    id.name = name;
    id.category = categoryForClient();
    return id;
}

std::string
SessionHelper::categoryForClient()
{
    if(!_router)
    {
       Glacier2::SessionNotExistException ex;
        throw ex;
    }
    return _category;
}

void SessionHelper::update()
{
	static std::string s1, s2;
	static std::string os1, os2;

	if(_initData->dispatcher){
		((IceDispatcher*)_initData->dispatcher.get())->update();
	}

	try {
        if (_router && !_destroy)
        {
            Ice::ConnectionIPtr con = Ice::ConnectionIPtr::dynamicCast(_router->ice_getConnection());

            if (!con || !con->isActiveOrHolding())
                destroy();
            else
            {
                IceUtil::Time now = IceUtil::Time::now();
                IceUtil::Time period = IceUtil::Time::seconds(_refreshPeriod);

                if (now - _stats->_lastReceiveTime > period && now - _stats->_lastSendTime  > period)
                {
                    cocos2d::CCLog("refresh session");
                    __incRef();
                    _router->begin_refreshSession(Glacier2::newCallback_Router_refreshSession(this, &SessionHelper::refreshException));
                    _stats->_lastSendTime = now;
                }
            }
        }
	} catch(...)
	{
		cocos2d::CCLog("SessionHelper::update exception!!!!!!!!!!!!!!!!!");
	}
}

void SessionHelper::refreshException(const Ice::Exception& ex)
{
    destroy();
}
/////////////////////////////////////////////////////////////////////////

SessionStats::SessionStats()
{
	_lastSendTime = IceUtil::Time::now();
	_lastReceiveTime = IceUtil::Time::now();
}

void SessionStats::bytesSent( const ::std::string&, ::Ice::Int )
{
	_lastSendTime = IceUtil::Time::now();
}

void SessionStats::bytesReceived( const ::std::string&, ::Ice::Int )
{
	_lastReceiveTime = IceUtil::Time::now();
}
